home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / FFConst / ffconst.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  18.4 KB  |  556 lines

  1. //-----------------------------------------------------------------------------
  2. // File: FFConst.cpp
  3. //
  4. // Desc: Demonstrates an application which sets a force feedback constant force 
  5. //       determined by the user.
  6. //
  7. // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #include <tchar.h>
  11. #include <windows.h>
  12. #include <windowsx.h>
  13. #include <basetsd.h>
  14. #include <mmsystem.h>
  15. #include <dinput.h>
  16. #include <math.h>
  17. #include "resource.h"
  18.  
  19.  
  20.  
  21.  
  22. //-----------------------------------------------------------------------------
  23. // Function prototypes 
  24. //-----------------------------------------------------------------------------
  25. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  26. BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, VOID* pContext );
  27. BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext );
  28. HRESULT InitDirectInput( HWND hDlg );
  29. VOID    FreeDirectInput();
  30. VOID    OnPaint( HWND hDlg );
  31. HRESULT OnMouseMove( HWND hDlg, INT x, INT y, UINT keyFlags );
  32. VOID    OnLeftButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags );
  33. VOID    OnLeftButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags );
  34. INT     CoordToForce( INT x );
  35. HRESULT SetDeviceForcesXY();
  36.  
  37.  
  38.  
  39.  
  40. //-----------------------------------------------------------------------------
  41. // Defines, constants, and global variables
  42. //-----------------------------------------------------------------------------
  43. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  44. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  45.  
  46. #define FEEDBACK_WINDOW_X       20
  47. #define FEEDBACK_WINDOW_Y       60
  48. #define FEEDBACK_WINDOW_WIDTH   200
  49.  
  50. LPDIRECTINPUT8        g_pDI       = NULL;         
  51. LPDIRECTINPUTDEVICE8  g_pDevice   = NULL;
  52. LPDIRECTINPUTEFFECT   g_pEffect   = NULL;
  53. BOOL                  g_bActive   = TRUE;
  54. DWORD                 g_dwNumForceFeedbackAxis = 0;
  55. INT                   g_nXForce;
  56. INT                   g_nYForce;
  57. DWORD                 g_dwLastEffectSet; // Time of the previous force feedback effect set
  58.  
  59.  
  60.  
  61.  
  62. //-----------------------------------------------------------------------------
  63. // Name: WinMain()
  64. // Desc: Entry point for the application.  Since we use a simple dialog for 
  65. //       user interaction we don't need to pump messages.
  66. //-----------------------------------------------------------------------------
  67. INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
  68. {
  69.     // Display the main dialog box.
  70.     DialogBox( hInst, MAKEINTRESOURCE(IDD_FORCE_FEEDBACK), NULL, MainDlgProc );
  71.     
  72.     return TRUE;
  73. }
  74.  
  75.  
  76.  
  77.  
  78. //-----------------------------------------------------------------------------
  79. // Name: MainDlgProc
  80. // Desc: Handles dialog messages
  81. //-----------------------------------------------------------------------------
  82. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  83. {
  84.     switch( msg ) 
  85.     {
  86.         case WM_INITDIALOG:
  87.             if( FAILED( InitDirectInput( hDlg ) ) ) 
  88.             {
  89.                 MessageBox( NULL, _T("Error Initializing DirectInput ") 
  90.                                   _T("The sample will now exit."), 
  91.                                   _T("FFConst"), MB_ICONERROR | MB_OK );
  92.                 EndDialog( hDlg, 0 );
  93.             }
  94.  
  95.             // Init the time of the last force feedback effect
  96.             g_dwLastEffectSet = timeGetTime();
  97.             break;
  98.  
  99.         case WM_MOUSEMOVE:
  100.             if( FAILED( OnMouseMove( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam ) ) )
  101.             {
  102.                 MessageBox( NULL, _T("Error setting effect parameters. ")
  103.                                   _T("The sample will now exit."), 
  104.                                   _T("FFConst"), MB_ICONERROR | MB_OK );
  105.                 EndDialog( hDlg, 0 );
  106.             }
  107.             break;
  108.  
  109.         case WM_LBUTTONDOWN:
  110.             OnLeftButtonDown( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam );
  111.             break;
  112.  
  113.         case WM_LBUTTONUP:
  114.             OnLeftButtonUp( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam );
  115.             break;
  116.  
  117.         case WM_PAINT:
  118.             OnPaint( hDlg );
  119.             break;
  120.  
  121.         case WM_ACTIVATE:
  122.             if( WA_INACTIVE != wParam && g_pDevice )
  123.             {
  124.                 // Make sure the device is acquired, if we are gaining focus.
  125.                 g_pDevice->Acquire();
  126.  
  127.                 if( g_pEffect ) 
  128.                     g_pEffect->Start( 1, 0 ); // Start the effect
  129.             }
  130.             break;
  131.  
  132.         case WM_COMMAND:
  133.             switch( LOWORD(wParam) )
  134.             {
  135.                 case IDCANCEL:
  136.                     EndDialog( hDlg, 0 );
  137.                     break;
  138.  
  139.                 default:
  140.                     return FALSE; // Message not handled 
  141.             }
  142.             break;
  143.  
  144.         case WM_DESTROY:
  145.             // Cleanup everything
  146.             KillTimer( hDlg, 0 );    
  147.             FreeDirectInput();    
  148.             break;
  149.  
  150.         default:
  151.             return FALSE; // Message not handled 
  152.     }
  153.  
  154.     return TRUE; // Message handled 
  155. }
  156.  
  157.  
  158.  
  159.  
  160. //-----------------------------------------------------------------------------
  161. // Name: InitDirectInput()
  162. // Desc: Initialize the DirectInput variables.
  163. //-----------------------------------------------------------------------------
  164. HRESULT InitDirectInput( HWND hDlg )
  165. {
  166.     DIPROPDWORD dipdw;
  167.     HRESULT     hr;
  168.  
  169.     // Register with the DirectInput subsystem and get a pointer
  170.     // to a IDirectInput interface we can use.
  171.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  172.                                          IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
  173.     {
  174.         return hr;
  175.     }
  176.     
  177.     // Look for a force feedback device we can use
  178.     if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, 
  179.                                          EnumFFDevicesCallback, NULL, 
  180.                                          DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK ) ) )
  181.     {
  182.         return hr;
  183.     }
  184.  
  185.     if( NULL == g_pDevice )
  186.     {
  187.         MessageBox( NULL, _T("Force feedback device not found. ")
  188.                           _T("The sample will now exit."), 
  189.                           _T("FFConst"), MB_ICONERROR | MB_OK );
  190.         EndDialog( hDlg, 0 );
  191.         return S_OK;
  192.     }
  193.  
  194.     // Set the data format to "simple joystick" - a predefined data format. A
  195.     // data format specifies which controls on a device we are interested in,
  196.     // and how they should be reported.
  197.     //
  198.     // This tells DirectInput that we will be passing a DIJOYSTATE structure to
  199.     // IDirectInputDevice8::GetDeviceState(). Even though we won't actually do
  200.     // it in this sample. But setting the data format is important so that the
  201.     // DIJOFS_* values work properly.
  202.     if( FAILED( hr = g_pDevice->SetDataFormat( &c_dfDIJoystick ) ) )
  203.         return hr;
  204.  
  205.     // Set the cooperative level to let DInput know how this device should
  206.     // interact with the system and with other DInput applications.
  207.     // Exclusive access is required in order to perform force feedback.
  208.     if( FAILED( hr = g_pDevice->SetCooperativeLevel( hDlg,
  209.                                                      DISCL_EXCLUSIVE | 
  210.                                                      DISCL_FOREGROUND ) ) )
  211.     {
  212.         return hr;
  213.     }
  214.  
  215.     // Since we will be playing force feedback effects, we should disable the
  216.     // auto-centering spring.
  217.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  218.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  219.     dipdw.diph.dwObj        = 0;
  220.     dipdw.diph.dwHow        = DIPH_DEVICE;
  221.     dipdw.dwData            = FALSE;
  222.  
  223.     if( FAILED( hr = g_pDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) )
  224.         return hr;
  225.  
  226.     // Enumerate and count the axes of the joystick 
  227.     if ( FAILED( hr = g_pDevice->EnumObjects( EnumAxesCallback, 
  228.                                               (VOID*)&g_dwNumForceFeedbackAxis, DIDFT_AXIS ) ) )
  229.         return hr;
  230.  
  231.     // This simple sample only supports one or two axis joysticks
  232.     if( g_dwNumForceFeedbackAxis > 2 )
  233.         g_dwNumForceFeedbackAxis = 2;
  234.  
  235.     // This application needs only one effect: Applying raw forces.
  236.     DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
  237.     LONG            rglDirection[2] = { 0, 0 };
  238.     DICONSTANTFORCE cf              = { 0 };
  239.  
  240.     DIEFFECT eff;
  241.     ZeroMemory( &eff, sizeof(eff) );
  242.     eff.dwSize                  = sizeof(DIEFFECT);
  243.     eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  244.     eff.dwDuration              = INFINITE;
  245.     eff.dwSamplePeriod          = 0;
  246.     eff.dwGain                  = DI_FFNOMINALMAX;
  247.     eff.dwTriggerButton         = DIEB_NOTRIGGER;
  248.     eff.dwTriggerRepeatInterval = 0;
  249.     eff.cAxes                   = g_dwNumForceFeedbackAxis;
  250.     eff.rgdwAxes                = rgdwAxes;
  251.     eff.rglDirection            = rglDirection;
  252.     eff.lpEnvelope              = 0;
  253.     eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
  254.     eff.lpvTypeSpecificParams   = &cf;
  255.     eff.dwStartDelay            = 0;
  256.  
  257.     // Create the prepared effect
  258.     if( FAILED( hr = g_pDevice->CreateEffect( GUID_ConstantForce, 
  259.                                               &eff, &g_pEffect, NULL ) ) )
  260.     {
  261.         return hr;
  262.     }
  263.  
  264.     if( NULL == g_pEffect )
  265.         return E_FAIL;
  266.  
  267.     return S_OK;
  268. }
  269.  
  270.  
  271.  
  272.  
  273. //-----------------------------------------------------------------------------
  274. // Name: EnumAxesCallback()
  275. // Desc: Callback function for enumerating the axes on a joystick and counting
  276. //       each force feedback enabled axis
  277. //-----------------------------------------------------------------------------
  278. BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
  279.                                 VOID* pContext )
  280. {
  281.     DWORD* pdwNumForceFeedbackAxis = (DWORD*) pContext;
  282.  
  283.     if( (pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
  284.         (*pdwNumForceFeedbackAxis)++;
  285.  
  286.     return DIENUM_CONTINUE;
  287. }
  288.  
  289.  
  290.  
  291.  
  292. //-----------------------------------------------------------------------------
  293. // Name: EnumFFDevicesCallback()
  294. // Desc: Called once for each enumerated force feedback device. If we find
  295. //       one, create a device interface on it so we can play with it.
  296. //-----------------------------------------------------------------------------
  297. BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, 
  298.                                      VOID* pContext )
  299. {
  300.     LPDIRECTINPUTDEVICE8 pDevice;
  301.     HRESULT              hr;
  302.  
  303.     // Obtain an interface to the enumerated force feedback device.
  304.     hr = g_pDI->CreateDevice( pInst->guidInstance, &pDevice, NULL );
  305.  
  306.     // If it failed, then we can't use this device for some
  307.     // bizarre reason.  (Maybe the user unplugged it while we
  308.     // were in the middle of enumerating it.)  So continue enumerating
  309.     if( FAILED(hr) ) 
  310.         return DIENUM_CONTINUE;
  311.  
  312.     // We successfully created an IDirectInputDevice8.  So stop looking 
  313.     // for another one.
  314.     g_pDevice = pDevice;
  315.  
  316.     return DIENUM_STOP;
  317. }
  318.  
  319.  
  320.  
  321.  
  322. //-----------------------------------------------------------------------------
  323. // Name: FreeDirectInput()
  324. // Desc: Initialize the DirectInput variables.
  325. //-----------------------------------------------------------------------------
  326. VOID FreeDirectInput()
  327. {
  328.     // Unacquire the device one last time just in case 
  329.     // the app tried to exit while the device is still acquired.
  330.     if( g_pDevice ) 
  331.         g_pDevice->Unacquire();
  332.     
  333.     // Release any DirectInput objects.
  334.     SAFE_RELEASE( g_pEffect );
  335.     SAFE_RELEASE( g_pDevice );
  336.     SAFE_RELEASE( g_pDI );
  337. }
  338.  
  339.  
  340.  
  341.  
  342. //-----------------------------------------------------------------------------
  343. // Name: OnPaint()
  344. // Desc: Handles the WM_PAINT window message
  345. //-----------------------------------------------------------------------------
  346. VOID OnPaint( HWND hDlg )
  347. {
  348.     PAINTSTRUCT ps;
  349.     HDC         hDC;
  350.     HPEN        hpenOld;
  351.     HPEN        hpenBlack;
  352.     HBRUSH      hbrOld;
  353.     HBRUSH      hbrBlack;
  354.     INT         x;
  355.     INT         y;
  356.     
  357.     hDC = BeginPaint( hDlg, &ps );
  358.     if( NULL == hDC ) 
  359.         return;
  360.  
  361.     // Everything is scaled to the size of the window.
  362.     hpenBlack = GetStockPen( BLACK_PEN );
  363.     hpenOld   = SelectPen( hDC, hpenBlack );
  364.  
  365.     // Draw force feedback bounding rect
  366.     MoveToEx( hDC, FEEDBACK_WINDOW_X, FEEDBACK_WINDOW_Y, NULL );
  367.  
  368.     LineTo( hDC, FEEDBACK_WINDOW_X, 
  369.                  FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH );
  370.     LineTo( hDC, FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH, 
  371.                  FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH );
  372.     LineTo( hDC, FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH, 
  373.                  FEEDBACK_WINDOW_Y );
  374.     LineTo( hDC, FEEDBACK_WINDOW_X, 
  375.                  FEEDBACK_WINDOW_Y );
  376.  
  377.     // Calculate center of feedback window for center marker
  378.     x = FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH / 2;
  379.     y = FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH / 2;
  380.  
  381.     // Draw center marker
  382.     MoveToEx( hDC, x, y - 10, NULL );
  383.     LineTo(   hDC, x, y + 10 + 1 );
  384.     MoveToEx( hDC, x - 10, y, NULL );
  385.     LineTo(   hDC, x + 10 + 1, y );
  386.  
  387.     hbrBlack = GetStockBrush( BLACK_BRUSH );
  388.     hbrOld   = SelectBrush( hDC, hbrBlack );
  389.  
  390.     x = MulDiv( FEEDBACK_WINDOW_WIDTH,
  391.                 g_nXForce + DI_FFNOMINALMAX, 
  392.                 2 * DI_FFNOMINALMAX );
  393.  
  394.     y = MulDiv( FEEDBACK_WINDOW_WIDTH, 
  395.                 g_nYForce + DI_FFNOMINALMAX, 
  396.                 2 * DI_FFNOMINALMAX );
  397.  
  398.     x += FEEDBACK_WINDOW_X;
  399.     y += FEEDBACK_WINDOW_Y;
  400.  
  401.     Ellipse( hDC, x-5, y-5, x+6, y+6 );
  402.  
  403.     SelectBrush( hDC, hbrOld );
  404.     SelectPen( hDC, hpenOld );
  405.  
  406.     EndPaint( hDlg, &ps );
  407. }
  408.  
  409.  
  410.  
  411.  
  412. //-----------------------------------------------------------------------------
  413. // Name: OnMouseMove()
  414. // Desc: If the mouse button is down, then change the direction of
  415. //       the force to match the new location.
  416. //-----------------------------------------------------------------------------
  417. HRESULT OnMouseMove( HWND hDlg, INT x, INT y, UINT keyFlags )
  418. {
  419.     HRESULT hr;
  420.     DWORD   dwCurrentTime;
  421.  
  422.     if( NULL == g_pEffect )
  423.         return S_OK;
  424.  
  425.     if( keyFlags & MK_LBUTTON ) 
  426.     {
  427.         dwCurrentTime = timeGetTime();
  428.         
  429.         if( dwCurrentTime - g_dwLastEffectSet < 100 )
  430.         {
  431.             // Don't allow setting effect more often than
  432.             // 100ms since every time an effect is set, the
  433.             // device will jerk.
  434.             //
  435.             // Note: This is not neccessary, and is specific to this sample
  436.             return S_OK;
  437.         }
  438.  
  439.         g_dwLastEffectSet = dwCurrentTime;
  440.  
  441.         x -= FEEDBACK_WINDOW_X;
  442.         y -= FEEDBACK_WINDOW_Y;
  443.  
  444.         g_nXForce = CoordToForce( x );
  445.         g_nYForce = CoordToForce( y );
  446.  
  447.         InvalidateRect( hDlg, 0, TRUE );
  448.         UpdateWindow( hDlg );
  449.  
  450.         if( FAILED( hr = SetDeviceForcesXY() ) )
  451.             return hr;
  452.     }
  453.  
  454.     return S_OK;
  455. }
  456.  
  457.  
  458.  
  459.  
  460. //-----------------------------------------------------------------------------
  461. // Name: OnLeftButtonDown()
  462. // Desc: Capture the mouse so we can follow it, and start updating the
  463. //       force information.
  464. //-----------------------------------------------------------------------------
  465. VOID OnLeftButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags )
  466. {
  467.     SetCapture( hDlg );
  468.     OnMouseMove( hDlg, x, y, MK_LBUTTON );
  469. }
  470.  
  471.  
  472.  
  473.  
  474. //-----------------------------------------------------------------------------
  475. // Name: OnLeftButtonUp()
  476. // Desc: Stop capturing the mouse when the button goes up.
  477. //-----------------------------------------------------------------------------
  478. VOID OnLeftButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags )
  479. {
  480.     ReleaseCapture();
  481. }
  482.  
  483.  
  484.  
  485.  
  486. //-----------------------------------------------------------------------------
  487. // Name: CoordToForce()
  488. // Desc: Convert a coordinate 0 <= nCoord <= FEEDBACK_WINDOW_WIDTH 
  489. //       to a force value in the range -DI_FFNOMINALMAX to +DI_FFNOMINALMAX.
  490. //-----------------------------------------------------------------------------
  491. INT CoordToForce( INT nCoord )
  492. {
  493.     INT nForce = MulDiv( nCoord, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH )
  494.                  - DI_FFNOMINALMAX;
  495.  
  496.     // Keep force within bounds
  497.     if( nForce < -DI_FFNOMINALMAX ) 
  498.         nForce = -DI_FFNOMINALMAX;
  499.  
  500.     if( nForce > +DI_FFNOMINALMAX ) 
  501.         nForce = +DI_FFNOMINALMAX;
  502.  
  503.     return nForce;
  504. }
  505.  
  506.  
  507.  
  508.  
  509. //-----------------------------------------------------------------------------
  510. // Name: SetDeviceForcesXY()
  511. // Desc: Apply the X and Y forces to the effect we prepared.
  512. //-----------------------------------------------------------------------------
  513. HRESULT SetDeviceForcesXY()
  514. {
  515.     // Modifying an effect is basically the same as creating a new one, except
  516.     // you need only specify the parameters you are modifying
  517.     LONG rglDirection[2] = { 0, 0 };
  518.  
  519.     DICONSTANTFORCE cf;
  520.  
  521.     if( g_dwNumForceFeedbackAxis == 1 )
  522.     {
  523.         // If only one force feedback axis, then apply only one direction and 
  524.         // keep the direction at zero
  525.         cf.lMagnitude = g_nXForce;
  526.         rglDirection[0] = 0;
  527.     }
  528.     else
  529.     {
  530.         // If two force feedback axis, then apply magnitude from both directions 
  531.         rglDirection[0] = g_nXForce;
  532.         rglDirection[1] = g_nYForce;
  533.         cf.lMagnitude = (DWORD)sqrt( (double)g_nXForce * (double)g_nXForce +
  534.                                      (double)g_nYForce * (double)g_nYForce );
  535.     }
  536.  
  537.     DIEFFECT eff;
  538.     ZeroMemory( &eff, sizeof(eff) );
  539.     eff.dwSize                = sizeof(DIEFFECT);
  540.     eff.dwFlags               = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  541.     eff.cAxes                 = g_dwNumForceFeedbackAxis;
  542.     eff.rglDirection          = rglDirection;
  543.     eff.lpEnvelope            = 0;
  544.     eff.cbTypeSpecificParams  = sizeof(DICONSTANTFORCE);
  545.     eff.lpvTypeSpecificParams = &cf;
  546.     eff.dwStartDelay            = 0;
  547.  
  548.     // Now set the new parameters and start the effect immediately.
  549.     return g_pEffect->SetParameters( &eff, DIEP_DIRECTION |
  550.                                            DIEP_TYPESPECIFICPARAMS |
  551.                                            DIEP_START );
  552. }
  553.  
  554.  
  555.  
  556.